/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.indexer.impl;

import java.util.logging.Logger;

import edu.ucsd.db.bassarisk.data.api.IValueType.IValueReference;
import edu.ucsd.db.bassarisk.data.values.IntType.IntTools;
import edu.ucsd.db.bassarisk.data.values.IntType.ObjectIntegerReference;
import edu.ucsd.db.bassarisk.data.values.LongType.LongTools;
import edu.ucsd.db.bassarisk.data.values.LongType.ObjectLongReference;
import edu.ucsd.db.bassarisk.indexer.api.IPosting.IPositionedPosting;
import edu.ucsd.db.bassarisk.indexer.api.IPosting.ISequencedPosting;
import edu.ucsd.db.bassarisk.indexer.api.IPostingFactory;

public class PostingFactory implements IPostingFactory {
  
  public static final PostingFactory INSTANCE = new PostingFactory();
  public final Logger LOG = Logger.getLogger(this.getClass().getName());
  
  private PostingFactory() {};

  private static final class PsnPostImpl implements IPositionedPosting {

    private final String groupKey;
    private final long position;
    private final String tokenKey;
    private final IValueReference<?> value;

    private PsnPostImpl(final String groupKey, final String tokenKey,
                        final IValueReference<?> value, final long position) {
      this.groupKey = groupKey;
      this.tokenKey = tokenKey;
      this.value = value;
      this.position = position;
    }

    @Override
    public String getGroupKey() {
      return this.groupKey;
    }

    @Override
    public long getPosition() {
      return this.position;
    }

    @Override
    public String getTokenKey() {
      return this.tokenKey;
    }

    @Override
    public IValueReference<?> getValue() {
      return this.value;
    }
    
    public String toString() {
      return String.format("PsnPosting group=%s, token=%s, value=%s, position=%s",
                           this.groupKey, this.tokenKey, this.value, this.position);
    }
    
    public boolean equals(IPositionedPosting other) {
      return this.getGroupKey().equals(other.getGroupKey()) &&
             this.getTokenKey().equals(other.getTokenKey()) &&
             (  (this.getValue() == null && other.getValue() == null) || 
                 this.getValue().equals(other.getValue())) &&
             this.getPosition() == other.getPosition();
    }

  }

  private static final class SeqPostImpl implements ISequencedPosting {

    private final boolean consumesSpace;
    private final String groupKey;
    private final String tokenKey;
    private final IValueReference<?> value;

    private SeqPostImpl(final String groupKey, final String tokenKey,
                        final IValueReference<?> value, final boolean consumesSpace) {
      this.groupKey = groupKey;
      this.tokenKey = tokenKey;
      this.value = value;
      this.consumesSpace = consumesSpace;
    }

    @Override
    public boolean consumesSpace() {
      return this.consumesSpace;
    }

    @Override
    public String getGroupKey() {
      return this.groupKey;
    }

    @Override
    public String getTokenKey() {
      return this.tokenKey;
    }

    @Override
    public IValueReference<?> getValue() {
      return this.value;
    }
    
    public String toString() {
      return String.format("PsnPosting group=%s, token=%s, value=%s, space=%s",
                           this.groupKey, this.tokenKey, this.value, this.consumesSpace);
    }
    
    public boolean equals(ISequencedPosting other) {
      return this.getGroupKey().equals(other.getGroupKey()) &&
             this.getTokenKey().equals(other.getTokenKey()) &&
             (  (this.getValue() == null && other.getValue() == null) || 
                 this.getValue().equals(other.getValue())) &&
             this.consumesSpace() == other.consumesSpace();
    }

  }

  @Override
  public ISequencedPosting int32ValuePosting(final String groupKey,
                                             final String tokenKey, final int value) {
    ObjectIntegerReference intRef = IntTools.INSTANCE.newReference();
    intRef.setValue(value);
    return new SeqPostImpl(groupKey, tokenKey, intRef, false);
  }

  @Override
  public IPositionedPosting int32ValuePosting(final String groupKey,
                                              final String tokenKey,
                                              final int value, final int position) {
    ObjectIntegerReference intRef = IntTools.INSTANCE.newReference();
    intRef.setValue(value);
    return new PsnPostImpl(groupKey, tokenKey, intRef, position);
  }

  @Override
  public ISequencedPosting
      int64ValuePosting(final String groupKey, final String tokenKey,
                        final long value) {
    ObjectLongReference longRef = LongTools.INSTANCE.newReference();
    longRef.setValue(value);
    return new SeqPostImpl(groupKey, tokenKey, longRef, false);
  }

  @Override
  public IPositionedPosting int64ValuePosting(final String groupKey,
                                              final String tokenKey,
                                              final long value, final int position) {
    ObjectLongReference longRef = LongTools.INSTANCE.newReference();
    longRef.setValue(value);
    return new PsnPostImpl(groupKey, tokenKey, longRef, position);
  }

  @Override
  public IPositionedPosting makeTokenPosting(final String groupKey,
                                             final String tokenKey,
                                             final int position) {
    return new PsnPostImpl(groupKey, tokenKey, null, position);
  }

  @Override
  public ISequencedPosting posting(final String groupKey, final String tokenKey,
                                   final IValueReference<?> value,
                                   final boolean consumesPosition) {
    return new SeqPostImpl(groupKey, tokenKey, value, consumesPosition);
  }

  @Override
  public IPositionedPosting posting(final String groupKey, final String tokenKey,
                                    final IValueReference<?> value,
                                    final int position) {
    return new PsnPostImpl(groupKey, tokenKey, value, position);
  }

  @Override
  public ISequencedPosting runEnd(final String groupKey) {
    return new SeqPostImpl(groupKey, "_end_", null, false);
  }

  @Override
  public IPositionedPosting runEnd(final String groupKey, final int position) {
    return new PsnPostImpl(groupKey, "_end_", null, position);
  }

  @Override
  public ISequencedPosting runStart(final String groupKey) {
    return new SeqPostImpl(groupKey, "_start_", null, false);
  }

  @Override
  public IPositionedPosting runStart(final String groupKey, final int position) {
    return new PsnPostImpl(groupKey, "_start_", null, position);
  }

  @Override
  public ISequencedPosting stringValuePosting(final String groupKey,
                                              final String tokenKey,
                                              final String value) {
    // FIXME
    LOG.severe("String values are not yet supported in bassarisk-storage.");
    return posting(groupKey, tokenKey, null, false);
  }

  @Override
  public IPositionedPosting
      stringValuePosting(final String groupKey, final String tokenKey,
                         final String value, final int position) {
    // FIXME
    LOG.severe("String values are not yet supported in bassarisk-storage.");
    return posting(groupKey, tokenKey, null, position);
  }

  @Override
  public ISequencedPosting tokenPosting(final String tokenKey) {
    return new SeqPostImpl("", tokenKey, null, true);
  }

  @Override
  public IPositionedPosting tokenPosting(final String tokenKey, final int position) {
    return new PsnPostImpl("", tokenKey, null, position);
  }

  @Override
  public ISequencedPosting
      tokenPosting(final String groupKey, final String tokenKey) {
    return new SeqPostImpl(groupKey, tokenKey, null, false);
  }

}
